Prediksi Harga Rumah

Pendahuluan

Seperti yang kita ketahui, harga rumah cenderung sangat beragam, ada yang relatif sangat mahal ataupun murah. Pada kenyataannya kita sulit menentukan seberapa besar harga rumah berdasarkan berbagai fitur-fitur atau fasilitas yang ada di dalam rumah. Kadang ada sebuah rumah yang memiliki luas lebih kecil dan sedikit kamar mandi dan sebagainya tetapi harganya relatif lebih mahal karena terletak di kota yang berbeda. Oleh karena itu, pada kesempatan ini saya mencoba membangun model yang dapat memprediksi harga rumah berdasarkan beberapa informasi fitur-fitur atau fasilitas yang ada dalam rumah alias menggunakan prediktor. Saya membangun model ini menggunakan dataset dari kaggle berikut.

Persiapan dan Wrangling Data

Pustaka

library(tidyverse)
library(caTools)
library(GGally)
library(ggplot2)
library(plotly)
library(rsample)
library(MLmetrics)
library(lmtest)
library(car)

Membaca Dataset

house <- read.csv("HousePrices_HalfMil.csv")
house

Deskripsi Kolom :

  • Area : Luas rumah atau bangunan.
  • Garage : Jumlah Garasi pada rumah.
  • FirePlace : Jumlah tempat perapian.
  • Baths : Jumlah Kamar mandi.
  • White.Marble : Apakah menggunakan marmer putih; 1 (ya) ; 0 (tidak).
  • Black.Marble : Apakah menggunakan marmer hitam; 1 (ya) ; 0 (tidak).
  • Indian.Marble : Apakah menggunakan marmer India; 1 (ya) ; 0 (tidak).
  • Floors : Apakah menggunakan keramik; 1 (ya) ; 0 (tidak).
  • City : Terletak di kota mana (Terdapat kategori 3, 2, 1)
  • Solar : Apakah menggunakan panel surya; 1 (ya) ; 0 (tidak).
  • Electric : Apakah menggunakan Electric ; 1 (ya) ; 0 (tidak).
  • Fiber : Apakah menggunakan Fiber ; 1 (ya) ; 0 (tidak).
  • Glass.Doors : Apakah terdapat Glass Doors ; 1 (ya) ; 0 (tidak).
  • Swiming.Pool : Apakah terdapat Swiming Pool ; 1 (ya) ; 0 (tidak).
  • Garden : Apakah terdapat Taman ; 1 (ya) ; 0 (tidak).
  • Prices : Harga rumah.

Mengecek Nilai Hilang

anyNA(house)
#> [1] FALSE

Tidak ada data yang hilang (missing value) sehingga kita dapat melanjutkan ke tahap selanjutnya.

Mengecek Duplikasi Data

anyDuplicated(house)
#> [1] 4416

Ternyata terdapat data yang terduplikasi sehingga sebaiknya kita menghapus data duplikasi tersebut dengan fungsi distinct()

house <- house %>% distinct()

anyDuplicated(house)
#> [1] 0

Asumsi Linearitas

ggcorr(data = house, label = T, hjust = 1, layout.exp = 3)

  • Variabel Indian.Marble, Black.Marble, White.Marble memiliki hubungan yang saling mempengaruhi sehingga sebaiknya memilih salah satu variabel aja agar tidak menghasilkan model redundan.

  • Variabel yang memiliki korelasi yang baik dengan kelas target (Prices) adalah Floors dan Fiber.

  • Terdapat pula variabel yang tidak memiliki hubungan dengan kelas target (Korelasinya = 0) yaitu Garden, Swiming.Pool dan Solar.

Mengecek Struktur Data

glimpse(house)
#> Rows: 494,307
#> Columns: 16
#> $ Area          <int> 164, 84, 190, 75, 148, 124, 58, 249, 243, 242, 61, 189, …
#> $ Garage        <int> 2, 2, 2, 2, 1, 3, 1, 2, 1, 1, 2, 2, 2, 3, 3, 3, 1, 3, 2,…
#> $ FirePlace     <int> 0, 0, 4, 4, 4, 3, 0, 1, 0, 2, 4, 0, 0, 3, 3, 4, 0, 3, 3,…
#> $ Baths         <int> 2, 4, 4, 4, 2, 3, 2, 1, 2, 4, 5, 4, 2, 3, 1, 1, 5, 3, 5,…
#> $ White.Marble  <int> 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,…
#> $ Black.Marble  <int> 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1,…
#> $ Indian.Marble <int> 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0,…
#> $ Floors        <int> 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1,…
#> $ City          <int> 3, 2, 2, 1, 2, 1, 3, 1, 1, 2, 1, 2, 1, 3, 3, 1, 3, 1, 3,…
#> $ Solar         <int> 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0,…
#> $ Electric      <int> 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1,…
#> $ Fiber         <int> 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,…
#> $ Glass.Doors   <int> 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1,…
#> $ Swiming.Pool  <int> 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0,…
#> $ Garden        <int> 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0,…
#> $ Prices        <int> 43800, 37550, 49500, 50075, 52400, 54300, 34400, 50425, …

Menyesuaikan Tipe Data

lapply(X = house[, 2:12],
       FUN = unique)
#> $Garage
#> [1] 2 1 3
#> 
#> $FirePlace
#> [1] 0 4 3 1 2
#> 
#> $Baths
#> [1] 2 4 3 1 5
#> 
#> $White.Marble
#> [1] 0 1
#> 
#> $Black.Marble
#> [1] 1 0
#> 
#> $Indian.Marble
#> [1] 0 1
#> 
#> $Floors
#> [1] 0 1
#> 
#> $City
#> [1] 3 2 1
#> 
#> $Solar
#> [1] 1 0
#> 
#> $Electric
#> [1] 1 0
#> 
#> $Fiber
#> [1] 1 0

Menghapus variable Black.Marble dan Indian.Marble untuk menghindari model redundan sehingga hanya menggunakan variable White.Marble karena variable ini dapat menghubungkan korelasi Black.Marble & Indian.Marble dengan Black.Marble & White.Marble.

Kita juga menyesuaikan tipe data factor untuk kolom yang memiliki nilai berulang.

house_clean <- 
  house %>% 
  select(-c(Black.Marble, Indian.Marble)) %>% 
  mutate(
   Garage = as.factor(Garage),
   FirePlace = as.factor(FirePlace),
   Baths = as.factor(Baths),
   White.Marble = as.factor(White.Marble),
   Floors = as.factor(Floors),
   City = as.factor(City),
   Solar = as.factor(Solar),
   Electric = as.factor(Electric),
   Glass.Doors = as.factor(Glass.Doors),
   Swiming.Pool = as.factor(Swiming.Pool),
   Garden = as.factor(Garden),
   Fiber = as.factor(Fiber)
) 

Eksplorasi Data Analisis

Persebaran Harga di Setiap Kota

box <- 
ggplot(data = house_clean, aes(x = City, y = Prices)) +
  geom_boxplot(aes(fill = City)) +
  labs(title = "Persebaran Harga Rumah",
       x = "City",
       y = "Price",
       fill = "City : ") +
  theme_classic() +
  theme(axis.text.x = element_blank())

ggplotly(box)

Persebaran Luas Daerah di Setiap Kota

box_1 <- 
ggplot(data = house_clean, aes(x = City, y = Area)) +
  geom_boxplot(aes(fill = City)) +
  labs(title = "Persebaran Luas Rumah",
       x = "City",
       y = "Luas",
       fill = "City : ") +
  theme_classic() +
  theme(axis.text.x = element_blank())

ggplotly(box_1)

Ringkasan Data

summary(house_clean)
#>       Area       Garage     FirePlace Baths     White.Marble Floors    
#>  Min.   :  1.0   1:164646   0:98448   1:99154   0:329720     0:247485  
#>  1st Qu.: 63.0   2:164374   1:98851   2:98643   1:164587     1:246822  
#>  Median :125.0   3:165287   2:98810   3:99063                          
#>  Mean   :124.9              3:99057   4:98885                          
#>  3rd Qu.:187.0              4:99141   5:98562                          
#>  Max.   :249.0                                                         
#>  City       Solar      Electric   Fiber      Glass.Doors Swiming.Pool
#>  1:164437   0:247812   0:246897   0:247005   0:247153    0:246965    
#>  2:164969   1:246495   1:247410   1:247302   1:247154    1:247342    
#>  3:164901                                                            
#>                                                                      
#>                                                                      
#>                                                                      
#>  Garden         Prices     
#>  0:246349   Min.   : 7725  
#>  1:247958   1st Qu.:33500  
#>             Median :41850  
#>             Mean   :42047  
#>             3rd Qu.:50750  
#>             Max.   :77975

Secara umum, semua kolom prediktor bertipe factor cenderung memiliki proporsi kelas seimbang seperti Baths, Fiber, Garage dll, kecuali kolom White.Marble yang memiliki perbedaan signifikan antara ya (1) dan tidak (0).

Pemodelan

Pemisahan Dataset

set.seed(100) # merujuk pada key untuk proses CV knn

index <- sample(nrow(house_clean), nrow(house_clean)*0.8)

train_house <- house_clean[index, ]
test_house <- house_clean[-index, ]
dim(train_house)
#> [1] 395445     14

Data latih terdiri dari 395,445 baris dengan 14 kolom.

dim(test_house)
#> [1] 98862    14

Data uji terdiri dari 98,862 baris dengan 14 kolom.

Simple Linear Regression

price_sm_model <- lm(formula = Prices ~ Floors, data = train_house)
summary(price_sm_model)
#> 
#> Call:
#> lm(formula = Prices ~ Floors, data = train_house)
#> 
#> Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -27205.4  -6980.4    -17.7   6582.3  28557.3 
#> 
#> Coefficients:
#>             Estimate Std. Error t value            Pr(>|t|)    
#> (Intercept) 34542.71      21.36  1617.3 <0.0000000000000002 ***
#> Floors1     15012.71      30.23   496.6 <0.0000000000000002 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 9505 on 395443 degrees of freedom
#> Multiple R-squared:  0.3841, Adjusted R-squared:  0.3841 
#> F-statistic: 2.466e+05 on 1 and 395443 DF,  p-value: < 0.00000000000000022

Kita mencoba melihat performa model Simple Linear Regression dengan prediktor Floors karena merupakan prediktor yang memiliki korelasi terkuat. R-squared yang dihasilkan model ini relatif belum baik yaitu R-squared : 0.3841.

Prediktor Multiple Linear Regression

Kita ingin mencoba menemukan prediktor mana saja yang akan memberikan ukuran model terbaik dan memenuhi asumsi regresi linear.

Pertimbangan Pertama, Step Wise Regression

  1. Model Seluruh Prediktor
test_model <- lm(formula = Prices ~., data = train_house)
summary(test_model)
#> 
#> Call:
#> lm(formula = Prices ~ ., data = train_house)
#> 
#> Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -2544.75 -2486.92     0.85  2494.86  2545.43 
#> 
#> Coefficients:
#>                  Estimate  Std. Error  t value            Pr(>|t|)    
#> (Intercept)    9740.05740    15.76995  617.634 <0.0000000000000002 ***
#> Area             25.01828     0.04522  553.245 <0.0000000000000002 ***
#> Garage2        1501.24961     7.96115  188.572 <0.0000000000000002 ***
#> Garage3        3009.45209     7.95040  378.529 <0.0000000000000002 ***
#> FirePlace1      747.08307    10.28219   72.658 <0.0000000000000002 ***
#> FirePlace2     1488.65778    10.28461  144.746 <0.0000000000000002 ***
#> FirePlace3     2241.72986    10.27300  218.216 <0.0000000000000002 ***
#> FirePlace4     3002.37790    10.27634  292.164 <0.0000000000000002 ***
#> Baths2         1268.66047    10.26942  123.538 <0.0000000000000002 ***
#> Baths3         2499.63998    10.25427  243.766 <0.0000000000000002 ***
#> Baths4         3740.43721    10.26155  364.510 <0.0000000000000002 ***
#> Baths5         5000.60876    10.27213  486.813 <0.0000000000000002 ***
#> White.Marble1 11503.74939     6.89002 1669.624 <0.0000000000000002 ***
#> Floors1       14995.81772     6.49409 2309.147 <0.0000000000000002 ***
#> City2          3498.29036     7.95420  439.804 <0.0000000000000002 ***
#> City3          6993.49926     7.95549  879.078 <0.0000000000000002 ***
#> Solar1          258.85140     6.49411   39.859 <0.0000000000000002 ***
#> Electric1      1246.96494     6.49405  192.017 <0.0000000000000002 ***
#> Fiber1        11753.39760     6.49413 1809.850 <0.0000000000000002 ***
#> Glass.Doors1   4456.01914     6.49408  686.167 <0.0000000000000002 ***
#> Swiming.Pool1    -1.92964     6.49408   -0.297               0.766    
#> Garden1           1.48882     6.49421    0.229               0.819    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 2042 on 395423 degrees of freedom
#> Multiple R-squared:  0.9716, Adjusted R-squared:  0.9716 
#> F-statistic: 6.438e+05 on 21 and 395423 DF,  p-value: < 0.00000000000000022
  1. Model dengan Step Wise Regression
model_backward <- step(object = test_model,
                       direction = "backward", 
                       trace = F)

summary(model_backward)
#> 
#> Call:
#> lm(formula = Prices ~ Area + Garage + FirePlace + Baths + White.Marble + 
#>     Floors + City + Solar + Electric + Fiber + Glass.Doors, data = train_house)
#> 
#> Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -2543.03 -2486.94     0.85  2494.92  2544.55 
#> 
#> Coefficients:
#>                  Estimate  Std. Error t value            Pr(>|t|)    
#> (Intercept)    9739.84265    15.09157  645.38 <0.0000000000000002 ***
#> Area             25.01829     0.04522  553.25 <0.0000000000000002 ***
#> Garage2        1501.24244     7.96111  188.57 <0.0000000000000002 ***
#> Garage3        3009.44900     7.95037  378.53 <0.0000000000000002 ***
#> FirePlace1      747.09180    10.28214   72.66 <0.0000000000000002 ***
#> FirePlace2     1488.65166    10.28457  144.75 <0.0000000000000002 ***
#> FirePlace3     2241.73255    10.27297  218.22 <0.0000000000000002 ***
#> FirePlace4     3002.38029    10.27630  292.17 <0.0000000000000002 ***
#> Baths2         1268.65375    10.26935  123.54 <0.0000000000000002 ***
#> Baths3         2499.64345    10.25424  243.77 <0.0000000000000002 ***
#> Baths4         3740.43320    10.26151  364.51 <0.0000000000000002 ***
#> Baths5         5000.60681    10.27209  486.81 <0.0000000000000002 ***
#> White.Marble1 11503.75178     6.89000 1669.63 <0.0000000000000002 ***
#> Floors1       14995.81646     6.49408 2309.15 <0.0000000000000002 ***
#> City2          3498.29098     7.95418  439.81 <0.0000000000000002 ***
#> City3          6993.50374     7.95545  879.08 <0.0000000000000002 ***
#> Solar1          258.84757     6.49404   39.86 <0.0000000000000002 ***
#> Electric1      1246.96589     6.49403  192.02 <0.0000000000000002 ***
#> Fiber1        11753.39126     6.49408 1809.86 <0.0000000000000002 ***
#> Glass.Doors1   4456.02149     6.49402  686.17 <0.0000000000000002 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 2042 on 395425 degrees of freedom
#> Multiple R-squared:  0.9716, Adjusted R-squared:  0.9716 
#> F-statistic: 7.115e+05 on 19 and 395425 DF,  p-value: < 0.00000000000000022

Rekomendasi Prediktor Step Wise Regression: Area + Garage + FirePlace + Baths + White.Marble + Floors + City + Solar + Electric + Fiber + Glass.Doors

Pertimbangan Kedua, Kategori Korelasi

  • Korelasi Kuat (cor : 0.5 - 0.69): Floors, Fiber
  • Korelasi Moderat (cor : 0.30 - 0.49): white.Marble
  • Korelasi Lemah (cor : 0.1 - 0.29): Glass.Doors, City, Electric, Baths, FirePlace, Garage, Area
  • Tidak Ada Hubungan (cor = 0) : Garden, Swiming.Pool, Solar

Kita tidak akan menggunakan prediktor yang memiliki korelasi = 0 (tidak ada hubungan).

Rekomendasi Prediktor Berdasarkan Korelasi: Area + Garage + FirePlace + Baths + White.Marble + Floors + City + Electric + Fiber + Glass.Doors

Pertimbangan Ketiga, Memenuhi Asumsi Distribusi Normal

test_model1 <- lm(formula = Prices ~ Area + Garage + FirePlace + Baths + White.Marble + 
    Floors + City + Electric + Fiber + Glass.Doors, data = train_house)

summary(test_model1)
#> 
#> Call:
#> lm(formula = Prices ~ Area + Garage + FirePlace + Baths + White.Marble + 
#>     Floors + City + Electric + Fiber + Glass.Doors, data = train_house)
#> 
#> Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -2663.33 -2370.36   -97.27  2379.56  2666.14 
#> 
#> Coefficients:
#>                  Estimate  Std. Error t value            Pr(>|t|)    
#> (Intercept)    9869.20068    14.76805  668.28 <0.0000000000000002 ***
#> Area             25.02010     0.04531  552.18 <0.0000000000000002 ***
#> Garage2        1501.49805     7.97707  188.23 <0.0000000000000002 ***
#> Garage3        3009.74207     7.96632  377.81 <0.0000000000000002 ***
#> FirePlace1      746.80966    10.30276   72.49 <0.0000000000000002 ***
#> FirePlace2     1487.87555    10.30518  144.38 <0.0000000000000002 ***
#> FirePlace3     2241.94392    10.29357  217.80 <0.0000000000000002 ***
#> FirePlace4     3001.41034    10.29688  291.49 <0.0000000000000002 ***
#> Baths2         1268.77058    10.28994  123.30 <0.0000000000000002 ***
#> Baths3         2499.73451    10.27480  243.29 <0.0000000000000002 ***
#> Baths4         3740.05387    10.28208  363.75 <0.0000000000000002 ***
#> Baths5         5000.64883    10.29269  485.85 <0.0000000000000002 ***
#> White.Marble1 11503.10384     6.90380 1666.20 <0.0000000000000002 ***
#> Floors1       14995.14817     6.50708 2304.44 <0.0000000000000002 ***
#> City2          3498.49984     7.97013  438.95 <0.0000000000000002 ***
#> City3          6993.93945     7.97140  877.38 <0.0000000000000002 ***
#> Electric1      1247.46588     6.50704  191.71 <0.0000000000000002 ***
#> Fiber1        11753.32055     6.50710 1806.23 <0.0000000000000002 ***
#> Glass.Doors1   4455.88060     6.50705  684.78 <0.0000000000000002 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 2046 on 395426 degrees of freedom
#> Multiple R-squared:  0.9715, Adjusted R-squared:  0.9715 
#> F-statistic: 7.48e+05 on 18 and 395426 DF,  p-value: < 0.00000000000000022

Memang secara performa sangat baik yaitu memiliki nilai Adjused R-squared : 0.9715. Namun, model ini sulit memenuhi asumsi Normality of Residuals yang sangat dibutuhkan dalam model linear ini. Model linear regression diharapkan menghasilkan error yang berdistribusi normal. Dengan begitu, error lebih banyak berkumpul di sekitar angka nol.

res <- as.data.frame(test_model1$residual)

ggplot(data = res, aes(x = res$`test_model1$residual`)) +
  geom_histogram(bins = 10, color="darkblue", fill="lightblue") + 
  labs(
    title = "Persebaran Residual Model",
    subtitle = "Sebelum Penyesuaian Prediktor",
    x =  "Residual",
    y = "Frekuensi") +
  scale_x_continuous(labels = scales::comma) +
  scale_y_continuous(labels = scales::comma) +
  theme_classic()

Sehingga saya mencoba eksperimen beberapa kali dan menghasilkan bahwa kita mesti membuang prediktor Area dan FirePlace dan kemudian berhasil menemukan model yang memenuhi asumsi Normality of Residuals yang terbukti dibagian akhir nanti. Selain itu, kedua prediktor tersebut dibuang tidak terlalu masalah karena memiliki kategori korelasi lemah.

Final Prediktor

Prediktor yang dipilih berikut : Floors, Fiber, White.Marble, City, Glass.Doors, Baths Electric Garage

Multiple Linear Regression

wt <- 1 / lm(abs(price_sm_model$residuals) ~ price_sm_model$fitted.values)$fitted.values^2

priceHo_multi_model <- lm(formula = Prices ~ Floors + Fiber + White.Marble + City + Glass.Doors + Baths + Electric + Garage, data = train_house, weights = wt)

summary(priceHo_multi_model)
#> 
#> Call:
#> lm(formula = Prices ~ Floors + Fiber + White.Marble + City + 
#>     Glass.Doors + Baths + Electric + Garage, data = train_house, 
#>     weights = wt)
#> 
#> Weighted Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -0.93665 -0.26899 -0.00013  0.26855  0.93723 
#> 
#> Coefficients:
#>                Estimate Std. Error t value            Pr(>|t|)    
#> (Intercept)   14492.443     17.101  847.46 <0.0000000000000002 ***
#> Floors1       14992.031      9.294 1613.15 <0.0000000000000002 ***
#> Fiber1        11760.315      9.294 1265.42 <0.0000000000000002 ***
#> White.Marble1 11514.184      9.860 1167.74 <0.0000000000000002 ***
#> City2          3495.719     11.383  307.10 <0.0000000000000002 ***
#> City3          6979.263     11.385  613.02 <0.0000000000000002 ***
#> Glass.Doors1   4452.961      9.294  479.14 <0.0000000000000002 ***
#> Baths2         1273.207     14.696   86.63 <0.0000000000000002 ***
#> Baths3         2497.610     14.675  170.20 <0.0000000000000002 ***
#> Baths4         3739.281     14.685  254.63 <0.0000000000000002 ***
#> Baths5         5001.354     14.700  340.22 <0.0000000000000002 ***
#> Electric1      1251.169      9.293  134.63 <0.0000000000000002 ***
#> Garage2        1504.119     11.393  132.02 <0.0000000000000002 ***
#> Garage3        3008.204     11.378  264.39 <0.0000000000000002 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 0.3768 on 395431 degrees of freedom
#> Multiple R-squared:  0.9418, Adjusted R-squared:  0.9418 
#> F-statistic: 4.922e+05 on 13 and 395431 DF,  p-value: < 0.00000000000000022

Intrepretasi Model :

  • Harga suatu rumah yang terletak di City1, dengan satu Kamar Mandi dan memiliki sebuah Garasi adalah 14,492.44 (Berdasarkan nilai Intercept = 14,492.44).
  • Cenderung terjadi peningkatan sebesar 14,992.031 apabila rumah menggunakan lantai Keramik (Berdasarkan nilai Floors1 = 14,992.031).
  • Cenderung terjadi peningkatan sebesar 11,760.315 apabila rumah menggunakan Fiber (Berdasarkan nilai Fiber1 = 11,760.315).
  • Cenderung terjadi peningkatan sebesar 11,514.184 apabila rumah menggunakan White Marble (Berdasarkan nilai White.Marble1 = 11,514.184).
  • Cenderung terjadi peningkatan sebesar 3,495.719 apabila rumah terletak di City2 (Berdasarkan nilai City2 = 3.495.719).
  • Cenderung terjadi peningkatan sebesar 6,979.263 apabila rumah terletak di City3 (Berdasarkan nilai City3 = 6,979.263).
  • Cenderung terjadi peningkatan sebesar 4,452.961 apabila rumah menggunakan Glass Door (Berdasarkan nilai Glass.Doors1 = 4452.961).
  • Cenderung terjadi peningkatan sebesar 1273.207 apabila rumah memiliki 2 buah Kamar Mandi (Berdasarkan nilai Baths2 = 1273.207).
  • Cenderung terjadi peningkatan sebesar 2497.610 apabila rumah memiliki 3 buah Kamar Mandi (Berdasarkan nilai Baths3 = 2497.610).
  • Cenderung terjadi peningkatan sebesar 3739.281 apabila rumah memiliki 4 buah Kamar Mandi (Berdasarkan nilai Baths4 = 3739.281).
  • Cenderung terjadi peningkatan sebesar 5001.354 apabila rumah memiliki 5 buah Kamar Mandi (Berdasarkan nilai Baths2 = 5001.354).
  • Cenderung terjadi peningkatan sebesar 1251.169 apabila rumah menggunakan listrik (Berdasarkan nilai Electric1 = 1251.169).
  • Cenderung terjadi peningkatan sebesar 1504.119 apabila rumah memiliki 2 buah Garasi (Berdasarkan nilai Garage2 = 1504.119).
  • Cenderung terjadi peningkatan sebesar 3008.204 apabila rumah memiliki 3 buah Garasi (Berdasarkan nilai Garage3 = 3008.204).

Model yang kita hasilkan sudah sangat baik. Hal ini terlihat pada semua prediktor yang digunakan memiliki 3 bintang (***) berarti predikornya signifikan.

Prediksi

Kita memprediksi menggunakan data test yang sudah kita pisahkan pada bagian awal modeling.

test_house$y_pred <- predict(object = priceHo_multi_model, newdata = test_house)
test_house

Kita melihat hasil prediksi model ini cukup beragam, terdapat data yang diprediksi mendekati nilai asli dan juga terdapat data yang diprediksi kurang mendekati nilai asli.

Evaluasi Model

R.Squared

summary(priceHo_multi_model)$adj.r.squared
#> [1] 0.9417953

Kesimpulannya, model priceHo_multi_model memiliki goodness of fit alias Adjusted R-squared yang sangat baik yaitu 0.9417953 (mendekati 1).

Error

RMSE(y_pred = test_house$y_pred, y_true = test_house$Prices)
#> [1] 2914.388

Interpretasi RMSE: akan terjadi error secara rata-rata sebesar 2914.388 pada setiap poin (titik data) prediksi.

Dalam mengetahui apakah RMSE sudah cukup kecil, biasa kita bandingkan dengan range variable targetnya :

range(test_house$Prices)
#> [1]  8275 77525

Dapat disimpulkan error model kita cukup kecil karena rentang data kita cukup besar yaitu (69.250).

Asumsi

1. Normality of Residuals

A. Secara Grafik

residual <- as.data.frame(priceHo_multi_model$residual)

ggplot(data = residual, aes(x = residual$`priceHo_multi_model$residual`)) +
  geom_histogram(bins = 10, color="darkblue", fill="lightblue") + 
  labs(
    title = "Persebaran Residual Model",
    subtitle = "Setelah Penyesuaian Prediktor",
    x =  "Residual",
    y = "Frekuensi") +
  scale_x_continuous(labels = scales::comma) +
  scale_y_continuous(labels = scales::comma) +
  theme_classic()

Terlihat secara grafik relatif error kita berdistribusi normal karena error memusat disekitar 0.

B. Dengan Kolmogorov-Smirnov Test

Uji statistik dengan ks.test()

  • alpha = 0.05

  • Kolmogorov-Smirnov hypothesis test:

    • H0: error berdistribusi normal (p-value > alpha)
    • H1: error TIDAK berdistribusi normal (p-value < alpha)
set.seed(100)
normal_data <-  rnorm(priceHo_multi_model$residuals)

ks.test(normal_data, "pnorm")
#> 
#>  Asymptotic one-sample Kolmogorov-Smirnov test
#> 
#> data:  normal_data
#> D = 0.001523, p-value = 0.3181
#> alternative hypothesis: two-sided

Kesimpulannya gagal tolak H0 karena p-value > alpha (0.3181 > 0.05) artinya error berdistribusi normal, asumsi terpenuhi.

2. Homoscedasticity of Residuals

A. Secara Grafik

# PErsiapan Dats
set.seed(100)
scatter <- data.frame(X = priceHo_multi_model$fitted.values,
                      Y = priceHo_multi_model$residuals)
nrow(scatter)
#> [1] 395445
# Sampel
index <- sample(nrow(scatter), nrow(scatter)*0.01)
scatter <- scatter[index,]
nrow(scatter)
#> [1] 3954

Kita mencoba melihat gambaran penyebaran fitted.values dan residuals. Tetapi kita hanya menggunakan 1% dari keseluruhan agar mempermudah melihat gambaran penyebarannya.

  • fitted.values adalah nilai hasil prediksi data training
  • residuals adalah nilai error (selisih aktual dengan prediksi)
# Visualisasi
ggplot(data = scatter, aes(x = X, y = Y)) +
  geom_point() +
  labs(
    title = "Penyebaran Fitted Values vs Residuals",
    subtitle = "Model Multi Linear Regression",
    x =  "Fitted Values",
    y = "Residuals") +
  scale_x_continuous(labels = scales::comma) +
  scale_y_continuous(labels = scales::comma) +
  theme_classic()

Terlihat secara grafik relatif error yang dihasilkan menyebar secara acak (tidak berpola) dalam proses pelatihan model.

B. Dengan Breusch-Pagan Test

Uji statistik dengan bptest()

  • alpha : 0.05

  • Breusch-Pagan hypothesis test:

    • H0: error menyebar konstan atau homoscedasticity (p-value > alpha)
    • H1: error menyebar TIDAK konstan atau heteroscedasticity (p-value < alpha)
bptest(priceHo_multi_model)
#> 
#>  studentized Breusch-Pagan test
#> 
#> data:  priceHo_multi_model
#> BP = 0.003011, df = 13, p-value = 1

Kesimpulannya gagal tolak H0 karena p-value > alpha (1 > 0.05) artinya error kita menyebar secara acak / tidak berpola (homoscedasticity of residuals), asumsi terpenuhi.

3. No Multicolinearity

Multicollinearity adalah kondisi adanya korelasi antar prediktor yang kuat. Hal ini tidak diinginkan karena menandakan prediktor redundan pada model, yang seharusnya dapat dipilih salah satu saja dari variable yang hubungannya amat kuat tersebut. Harapannya tidak terjadi multicollinearity. Kita menggunakan uji VIF (Variance Inflation Factor) untuk menguji asumsi No Multicolinearity.

Uji VIF dengan fungsi vif()

Variance Inflation Factor hypothesis test:

  • nilai VIF atau > 10: terjadi multicollinearity pada model
  • nilai VIF < 10: tidak terjadi multicollinearity pada model
vif(priceHo_multi_model)
#>                  GVIF Df GVIF^(1/(2*Df))
#> Floors       1.000032  1        1.000016
#> Fiber        1.000036  1        1.000018
#> White.Marble 1.000035  1        1.000018
#> City         1.000103  2        1.000026
#> Glass.Doors  1.000029  1        1.000015
#> Baths        1.000118  4        1.000015
#> Electric     1.000011  1        1.000006
#> Garage       1.000081  2        1.000020

Berhubung kolom prediktor berjenis factor sehingga kita mengunakan nilai **GVIF^(1/2*Df)** di atas untuk mengantikan nilai VIF yang memang tidak muncul.

Kesimpulannya model kita No Multicollinearity karena setiap predktor memiliki nilai GVIF^(1/2*Df) kurang dari 10, asumsi terpenuhi.

Kesimpulan

Kita telah melalui berbagai proses dari menyiapkan data hingga membuktikan beberapa asumsi dalam regresi linear. Seluruh keseluruhan kita telah berhasil membangun sebuah model yang dapat digunakan untuk memprediksi harga suatu rumah berdasarkan 9 prediktor yang baik. Model kita juga telah dievaluasi kemudian menghasilkan nilai adj.R-Squared yang sangat tinggi dan nilai error yang relatif kecil dibandingkan range harga.. Model kita juga telah berhasil memenuhi 4 asumsi (satu sebelum pembangunan model dan tiga setelah pembangunan model). Harapannya melalui projek ini dapat menjadi referensi dalam membangun model untuk memprediksi harga suatu rumah. Sekian terima kasih banyak yang telah melihat projek ini. Saya juga menyukai masukan dan kolaborasi sehingga apabila ada masukan, saran atau untuk berkolaborasi dapat menghubungi lewat Linkedin.